/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2009，所有权利保留。
 * 
 * 项目名：	rumba-widget2
 * 文件名：	RToggleButton2.java
 * 模块说明：	
 * 修改历史：
 * Feb 2, 2009 - lxm - 创建。
 */
package com.hd123.rumba.gwt.widget2.client.button;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasAllFocusHandlers;
import com.google.gwt.event.dom.client.HasAllKeyHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HasFocus;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.ListenerWrapper;
import com.google.gwt.user.client.ui.SourcesClickEvents;
import com.google.gwt.user.client.ui.SourcesKeyboardEvents;
import com.hd123.rumba.gwt.widget2.client.action.IsRCheckActionControl;
import com.hd123.rumba.gwt.widget2.client.action.RCheckAction;
import com.hd123.rumba.gwt.widget2.client.hotkey.HasRHotKey;
import com.hd123.rumba.gwt.widget2.client.hotkey.RHotKey;
import com.hd123.rumba.gwt.widget2.client.hotkey.RHotKeyManager;
import com.hd123.rumba.gwt.widget2.client.misc.RImageLocation;

/**
 * 按下/弹出式按钮控件。
 * <p>
 * 
 * <b>控件元素结构</b><br>
 * FocusPanel(div)
 * <p>
 * 
 * <b>CSS Style Rules</b> <ol type=disc>
 * <li>.rb-ToggleButton { 整个按钮 }</li>
 * <li>.rb-ToggleButton-up { 按钮的弹出状态 }</li>
 * <li>.rb-ToggleButton-up-hovering { 按钮的弹出且鼠标位于控件上方状态 }</li>
 * <li>.rb-ToggleButton-up-disabled { 按钮的弹出且不可用状态 }</li>
 * <li>.rb-ToggleButton-down { 按钮的按下状态 }</li>
 * <li>.rb-ToggleButton-down-hovering { 按钮的按下且鼠标位于控件上方状态 }</li>
 * <li>.rb-ToggleButton-down-disabled { 按钮的按下且不可用状态 }</li> </ol>
 * 
 * @author lxm
 * 
 */
@SuppressWarnings("deprecation")
public class RToggleButton extends Composite implements HasHTML, IsRButton, HasFocus, Focusable,
    HasAllFocusHandlers, SourcesKeyboardEvents, HasAllKeyHandlers, SourcesClickEvents,
    HasClickHandlers, IsRCheckActionControl, HasRHotKey {

  private static final String STYLENAME_DEFAULT = "rb-ToggleButton";

  /**
   * 创建对象实例。
   */
  public RToggleButton() {
    super();
    drawSelf();
  }

  /**
   * 创建对象实例。
   * 
   * @param action
   *          指定对应的动作控件对象。
   */
  public RToggleButton(RCheckAction action) {
    this();
    setAction(action);
  }

  public RToggleButton(String text) {
    this();
    setText(text);
  }

  /**
   * @deprecated 被{@link #RToggleButton(String, ClickHandler)}代替。
   */
  public RToggleButton(String text, ClickListener listener) {
    this(text);
    addClickListener(listener);
  }

  public RToggleButton(String text, ClickHandler handler) {
    this(text);
    addClickHandler(handler);
  }

  private FocusPanel pnl;
  private String caption;
  private RImageLocation icon;
  private RImageLocation hoveringIcon;
  private RImageLocation disabledIcon;
  private RImageLocation downIcon;
  private RImageLocation downHoveringIcon;
  private RImageLocation downDisabledIcon;
  private boolean enabled = true;
  private boolean down = false;
  private boolean showIcon = true;
  private boolean showText = true;
  private RCheckAction action;
  private boolean hovering = false;
  private String title;
  private RHotKey hotKey;

  /** 按钮标题文本 */
  public String getHTML() {
    return getText();
  }

  /** 按钮标题文本 */
  public void setHTML(String html) {
    setText(html);
  }

  /** 按钮标题文本 */
  public String getText() {
    return caption;
  }

  /** 按钮标题文本 */
  public void setText(String text) {
    caption = text;
    refreshInner();
  }

  /** 弹出且可用状态下的图标。 */
  public RImageLocation getIcon() {
    return icon;
  }

  /** 弹出且可用状态下的图标。 */
  public void setIcon(RImageLocation icon) {
    this.icon = icon;
    refreshInner();
  }

  /** 弹出且当鼠标移动到控件上方时显示的图标。null表示显示{@link #getIcon()}。 */
  public RImageLocation getHoveringIcon() {
    return hoveringIcon;
  }

  /** 弹出且当鼠标移动到控件上方时显示的图标。null表示显示{@link #getIcon()}。 */
  public void setHoveringIcon(RImageLocation hoveringIcon) {
    this.hoveringIcon = hoveringIcon;
    refreshInner();
  }

  /** 弹出且不可用状态时显示的图标。null表示显示{@link #getIcon()}。 */
  public RImageLocation getDisabledIcon() {
    return disabledIcon;
  }

  /** 弹出且不可用状态时显示的图标。null表示显示{@link #getIcon()}。 */
  public void setDisabledIcon(RImageLocation disabledIcon) {
    this.disabledIcon = disabledIcon;
    refreshInner();
  }

  /** 按下且可用状态时显示的图标。null表示显示{@link #getIcon()}。 */
  public RImageLocation getDownIcon() {
    return downIcon;
  }

  /** 按下且可用状态时显示的图标。null表示显示{@link #getIcon()}。 */
  public void setDownIcon(RImageLocation downIcon) {
    this.downIcon = downIcon;
    refreshInner();
  }

  /** 按下且当鼠标移动到控件上方时显示的图标。null表示显示{@link #getDownIcon()}。 */
  public RImageLocation getDownHoveringIcon() {
    return downHoveringIcon;
  }

  /** 按下且当鼠标移动到控件上方时显示的图标。null表示显示{@link #getDownIcon()}。 */
  public void setDownHoveringIcon(RImageLocation downHoveringIcon) {
    this.downHoveringIcon = downHoveringIcon;
    refreshInner();
  }

  /** 按下且不可用状态时显示的图标。null表示显示{@link #getDownIcon()}。 */
  public RImageLocation getDownDisabledIcon() {
    return downDisabledIcon;
  }

  /** 按下且不可用状态时显示的图标。null表示显示{@link #getDownIcon()}。 */
  public void setDownDisabledIcon(RImageLocation downDisabledIcon) {
    this.downDisabledIcon = downDisabledIcon;
    refreshInner();
  }

  /** 是否可用状态。 */
  public boolean isEnabled() {
    return enabled;
  }

  /** 是否可用状态。 */
  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
    refresh();
  }

  /** 是否处于按下状态。 */
  public boolean isDown() {
    return down;
  }

  /** 是否处于按下状态。 */
  public void setDown(boolean down) {
    this.down = down;
    refreshInner();
    refreshStyle();
  }

  public boolean isShowIcon() {
    return showIcon;
  }

  public void setShowIcon(boolean showIcon) {
    this.showIcon = showIcon;
    if (!this.showIcon && !showText)
      showText = true;
    refreshInner();
  }

  public boolean isShowText() {
    return showText;
  }

  public void setShowText(boolean showText) {
    this.showText = showText;
    if (!this.showText && !showIcon)
      showIcon = true;
    refreshInner();
  }

  public int getTabIndex() {
    return pnl.getTabIndex();
  }

  public void setTabIndex(int index) {
    pnl.setTabIndex(index);
  }

  public void setAccessKey(char key) {
    pnl.setAccessKey(key);
  }

  public void setFocus(boolean focused) {
    pnl.setFocus(focused);
  }

  /**
   * @deprecated 被{@link #addFocusHandler(FocusHandler)}与
   *             {@link #addBlurHandler(BlurHandler)}替代。
   */
  public void addFocusListener(FocusListener listener) {
    ListenerWrapper.WrappedFocusListener.add(this, listener);
  }

  /**
   * @deprecated 被{@link #addFocusHandler(FocusHandler)}与
   *             {@link #addBlurHandler(BlurHandler)}返回的
   *             {@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeFocusListener(FocusListener listener) {
    ListenerWrapper.WrappedFocusListener.remove(this, listener);
  }

  public HandlerRegistration addFocusHandler(FocusHandler handler) {
    return addDomHandler(handler, FocusEvent.getType());
  }

  public HandlerRegistration addBlurHandler(BlurHandler handler) {
    return addDomHandler(handler, BlurEvent.getType());
  }

  /**
   * @deprecated 被{@link #addKeyDownHandler(KeyDownHandler)}、
   *             {@link #addKeyPressHandler(KeyPressHandler)} 和
   *             {@link #addKeyUpHandler(KeyUpHandler)}替代。
   */
  public void addKeyboardListener(KeyboardListener listener) {
    ListenerWrapper.WrappedKeyboardListener.add(this, listener);
  }

  /**
   * @deprecated 被{@link #addKeyDownHandler(KeyDownHandler)}、
   *             {@link #addKeyPressHandler(KeyPressHandler)} 和
   *             {@link #addKeyUpHandler(KeyUpHandler)}所返回的
   *             {@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeKeyboardListener(KeyboardListener listener) {
    ListenerWrapper.WrappedKeyboardListener.remove(this, listener);
  }

  public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
    return addDomHandler(handler, KeyDownEvent.getType());
  }

  public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
    return addDomHandler(handler, KeyUpEvent.getType());
  }

  public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
    return addDomHandler(handler, KeyPressEvent.getType());
  }

  /**
   * @deprecated 被{@link #addClickHandler(ClickHandler)}替代。
   */
  public void addClickListener(ClickListener listener) {
    ListenerWrapper.WrappedClickListener.add(this, listener);
  }

  /**
   * @deprecated 被{@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeClickListener(ClickListener listener) {
    ListenerWrapper.WrappedClickListener.remove(this, listener);
  }

  public HandlerRegistration addClickHandler(ClickHandler handler) {
    return addDomHandler(handler, ClickEvent.getType());
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
    refreshTitle();
  }

  /**
   * 根据title和热键刷新提示信息
   * 
   */
  protected void refreshTitle() {
    StringBuffer s = new StringBuffer();
    if (title != null)
      s.append(title);

    RHotKey hKey = null;
    if (hotKey != null)
      hKey = hotKey;
    else if (action != null)
      hKey = action.getHotKey();
    if (hKey != null) {
      if (s.length() > 0)
        s.append(" ");
      s.append(hKey.toString());
    }

    super.setTitle(s.toString());
  }

  public RCheckAction getAction() {
    return action;
  }

  public void setAction(RCheckAction action) {
    if (action == null) {
      if (this.action != null)
        this.action.removeControl(this);
    }

    this.action = action;

    if (this.action != null) {
      if (this.action.containsControl(this))
        return;
      this.action.addControl(this);
    }
  }

  public void applyAction() {
    if (action == null)
      return;
    caption = action.getCaption();
    setTitle(action.getTitle());
    enabled = action.isEnabled();
    down = action.isChecked();

    icon = action.getIcon();
    hoveringIcon = action.getHoveringIcon();
    disabledIcon = action.getDisabledIcon();
    downIcon = action.getCheckedIcon();
    downHoveringIcon = action.getCheckedHoveringIcon();
    downDisabledIcon = action.getCheckedDisabledIcon();
    refresh();
  }

  public void applyChecked() {
    if (action == null)
      return;
    setDown(action.isChecked());
  }

  public RHotKey getHotKey() {
    return hotKey;
  }

  public void setHotKey(RHotKey hotKey) {
    if (this.hotKey == hotKey)
      return;

    this.hotKey = hotKey;
    RHotKeyManager.remove(this);
    if (this.hotKey != null) {
      RHotKeyManager.add(this.hotKey, this, new Command() {
        public void execute() {
          if (enabled) {
            doClick();
            DomEvent.fireNativeEvent(Document.get().createClickEvent(0, 0, 0, 0, 0, false, false,
                false, false), RToggleButton.this);
          }
        }
      });
    }

    refreshTitle();
  }

  public void clearHotKey() {
    setHotKey(null);
  }

  /** 是否鼠标位于控件上方。 */
  protected boolean isHovering() {
    return hovering;
  }

  /** 是否鼠标位于控件上方。 */
  protected void setHovering(boolean hovering) {
    this.hovering = hovering;
    refreshInner();
    refreshStyle();
  }

  protected RImageLocation getCurrIcon() {
    return icon;
  }

  protected RImageLocation getCurrHoveringIcon() {
    return hoveringIcon != null ? hoveringIcon : getCurrIcon();
  }

  protected RImageLocation getCurrDisabledIcon() {
    return disabledIcon != null ? disabledIcon : getCurrIcon();
  }

  protected RImageLocation getCurrDownIcon() {
    return downIcon != null ? downIcon : getCurrIcon();
  }

  protected RImageLocation getCurrDownHoveringIcon() {
    return downHoveringIcon != null ? downHoveringIcon : getCurrDownIcon();
  }

  protected RImageLocation getCurrDownDisabledIcon() {
    return downDisabledIcon != null ? downDisabledIcon : getCurrDownIcon();
  }

  private void drawSelf() {
    pnl = new FocusPanel();
    Handler_pnl handler = new Handler_pnl();
    pnl.addClickHandler(handler);
    pnl.addMouseOverHandler(handler);
    pnl.addMouseOutHandler(handler);
    pnl.addKeyPressHandler(handler);
    initWidget(pnl);
    setStylePrimaryName(STYLENAME_DEFAULT);
    setStyleName(STYLENAME_DEFAULT);
  }

  private void refresh() {
    refreshEnabled();
    refreshInner();
    refreshStyle();
  }

  private void refreshEnabled() {
    if (enabled)
      DOM.removeElementAttribute(pnl.getElement(), "disabled");
    else
      DOM.setElementAttribute(pnl.getElement(), "disabled", "true");
  }

  private void refreshInner() {
    StringBuffer sb = new StringBuffer();

    if (showIcon) {
      // 根据状态取得当前图标
      RImageLocation displayIcon = null;
      if (!down) {
        displayIcon = getCurrIcon();
        if (!enabled)
          displayIcon = getCurrDisabledIcon();
        else if (hovering)
          displayIcon = getCurrHoveringIcon();
      } else {
        displayIcon = getCurrDownIcon();
        if (!enabled)
          displayIcon = getCurrDownDisabledIcon();
        else if (hovering)
          displayIcon = getCurrDownHoveringIcon();
      }

      if (displayIcon != null)
        sb.append(displayIcon.toHTML());
    }

    if (showText) {
      if (sb.length() > 0)
        sb.append(" ");
      sb.append(caption);
    }

    String newHtml = sb.toString();
    String oldHtml = DOM.getInnerHTML(pnl.getElement());

    if (!newHtml.equals(oldHtml))
      DOM.setInnerHTML(pnl.getElement(), sb.toString());
  }

  private void refreshStyle() {
    String newStyleName = getStylePrimaryName() + " " + getStylePrimaryName();
    newStyleName += down ? "-down" : "-up";
    if (!enabled)
      newStyleName += "-disabled";
    else if (hovering)
      newStyleName += "-hovering";

    String oldStyleName = getStyleName();
    if (!newStyleName.equals(oldStyleName))
      setStyleName(newStyleName);
  }

  private void doClick() {
    if (!isEnabled())
      return;
    setDown(!isDown());
    if (action != null) {
      action.setChecked(isDown());
      action.fireClick();
    }
  }

  private class Handler_pnl implements MouseOverHandler, MouseOutHandler, ClickHandler,
      KeyPressHandler {

    public void onMouseOver(MouseOverEvent event) {
      if (isEnabled())
        setHovering(true);
    }

    public void onMouseOut(MouseOutEvent event) {
      if (isEnabled())
        setHovering(false);
    }

    public void onClick(ClickEvent event) {
      doClick();
    }

    public void onKeyPress(KeyPressEvent event) {
      char keyCode = event.getCharCode();
      if (keyCode == KeyCodes.KEY_ENTER || keyCode == '\r' || keyCode == ' ') {
        doClick();
        NativeEvent ne = Document.get().createClickEvent(1, 0, 0, 0, 0, false, false, false, false);
        //        RToggleButton.this.getElement().dispatchEvent(ne);
        DomEvent.fireNativeEvent(ne, RToggleButton.this);
      }
    }

  }
}
